#include <arm_neon.h>
#include <stdio.h>
#include <sys/time.h> 


void neon_multiply(float *a, float *b, float *c, int size) {
    for (int i = 0; i < size; i += 4) {
        float32x4_t va = vld1q_f32(a + i);
        float32x4_t vb = vld1q_f32(b + i);
        float32x4_t vc = vmulq_f32(va, vb);
        vst1q_f32(c + i, vc);
    }
}

void multiply(float *a, float *b, float *c, int size) {
    for (int i = 0; i < size; i++) {
        c[i] = a[i] * b[i];
        
    }
}

void neon_add(float *a, float *b, float *c, int size) {
    for (int i = 0; i < size; i += 16) {
        float32x4_t va = vld1q_f32(a + i);
        float32x4_t va1 = vld1q_f32(a + i + 4);
        float32x4_t va2 = vld1q_f32(a + i + 8);
        float32x4_t va3 = vld1q_f32(a + i + 12);


        float32x4_t vb = vld1q_f32(b + i);
        float32x4_t vb1 = vld1q_f32(b + i + 4);
        float32x4_t vb2 = vld1q_f32(b + i + 8);
        float32x4_t vb3 = vld1q_f32(b + i + 12);

        float32x4_t vc = vaddq_f32(va, vb);
        float32x4_t vc1 = vaddq_f32(va1, vb1);
        float32x4_t vc2 = vaddq_f32(va2, vb2);
        float32x4_t vc3 = vaddq_f32(va3, vb3);

        
        

        // float32x4_t va4 = vld1q_f32(a + i + 16);
        // float32x4_t vb4 = vld1q_f32(b + i + 16);
        // float32x4_t vc4 = vaddq_f32(va4, vb4);
        

        // float32x4_t va5 = vld1q_f32(a + i + 20);
        // float32x4_t vb5 = vld1q_f32(b + i + 20);
        // float32x4_t vc5 = vaddq_f32(va5, vb5);
        

        // float32x4_t va6 = vld1q_f32(a + i + 24);
        // float32x4_t vb6 = vld1q_f32(b + i + 24);
        // float32x4_t vc6 = vaddq_f32(va6, vb6);
        

        // float32x4_t va7 = vld1q_f32(a + i + 28);
        // float32x4_t vb7 = vld1q_f32(b + i + 28);
        // float32x4_t vc7 = vaddq_f32(va7, vb7);


        vst1q_f32(c + i, vc);
        vst1q_f32(c + i + 4, vc1);
        vst1q_f32(c + i + 8, vc2);
        vst1q_f32(c + i + 12, vc3);
        // vst1q_f32(c + i + 16, vc4);
        // vst1q_f32(c + i + 20, vc5);
        // vst1q_f32(c + i + 24, vc6);
        // vst1q_f32(c + i + 28, vc7);

        // float32x4_t va8 = vld1q_f32(a + i + 32);
        // float32x4_t vb8 = vld1q_f32(b + i + 32);
        // float32x4_t vc8 = vaddq_f32(va8, vb8);
        // vst1q_f32(c + i + 32, vc8);

        // float32x4_t va9 = vld1q_f32(a + i + 36);
        // float32x4_t vb9 = vld1q_f32(b + i + 36);
        // float32x4_t vc9 = vaddq_f32(va9, vb9);
        // vst1q_f32(c + i + 36, vc9);

        
    }
}



void add(float *a, float *b, float *c, int size)
{
    for (int i = 0; i < size; i++) {
        c[i] = a[i] + b[i];
    }
}



int main() {
    const int size = 8;
    float a[size] = {0};
    float b[size] = {0};

    for (int i = 0;i < size;i++)
    {
        a[i] = i + 1;//1 2 3 4 5 6 7 8
        b[i] = i + 3;//3 4 5 6 7 8 9 10
    }

    // 从起始地址a load Q寄存器大小的数据 类型为float32x4_t
    float32x4_t k1 = vld1q_f32(a);
    float32x4_t k2 = vld1q_f32(b);
    // 2个float32x4_t 
    float32x4x2_t k3 = {vdupq_n_f32(1.2),vdupq_n_f32(0.0)};

    // 把k3.val[1] 的 0位置的数据修改后并返回k3.val[1] ,可以用于对某一个向量数据的lane修改，也可以用于将一个向量数据修改某一lane值后返回
    k3.val[1] = vsetq_lane_f32(1.1,k3.val[1],0);
    printf("value: %f \n",vgetq_lane_f32(k3.val[1],0));
    // 分别取每个Q寄存器中的内容
    k3.val[0] = k1;
    k3.val[1] = k2;

    // 从起始地址b load 2个Q寄存器大小的数据 类型为float32x4x2_t
    k3 = vld2q_f32(b);

    // 从Q寄存器中去编号为0处的float类型的值
    printf("value: %f \n",vgetq_lane_f32(k3.val[0],0));


    float32x4_t b1 = vdupq_n_f32(4.5);
    printf("b1: [%f %f %f %f] \n",vgetq_lane_f32(b1,0),vgetq_lane_f32(b1,1),vgetq_lane_f32(b1,2),vgetq_lane_f32(b1,3));
    float32x2_t b2 = vdup_n_f32(5.6);
    b2 = vset_lane_f32(3.4,b2,0);
    printf("b2: [%f %f] \n",vget_lane_f32(b2,0),vget_lane_f32(b2,1));

    //取D寄存器类型位置0的值，赋值给Q寄存器类型数据的每个lane
    b1 = vdupq_lane_f32(b2,0);

    printf("b1: [%f %f %f %f] \n",vgetq_lane_f32(b1,0),vgetq_lane_f32(b1,1),vgetq_lane_f32(b1,2),vgetq_lane_f32(b1,3));


    // vaddvq_f32(u1) 向量寄存器中的每个lane的值进行累加
    float32x4_t u1 = vdupq_n_f32(2.1);
    float sum = vaddvq_f32(u1);
    printf("sum: %f \n",sum);

    // 返回一个数组中的最大最小
    float32x4_t h1 = {1.0,2.0,8.0,4.0};
    float h1_max = vmaxvq_f32(h1);
    printf("h1_max: %f \n",h1_max);

    // 取最大最小
    float32x4_t s1 = {1.1,2.1,3.1,4.1};
    float32x4_t s2 = {0.1,3.2,5.6,1.0};
    float32x4_t s_max = vmaxq_f32(s1,s2); // 最小vminq_f32(s1,s2)
    printf("s_max: [%f %f %f %f] \n",vgetq_lane_f32(s_max,0),vgetq_lane_f32(s_max,1),vgetq_lane_f32(s_max,2),vgetq_lane_f32(s_max,3));


    // 得到a+b*c
    float32x4_t d1 = {1.1,2.1,3.1,4.1};
    float32x4_t d2 = {2.0,3.0,4.0,5.0};
    float32x4_t d3 = {2.0,2.0,2.0,2.0};
    float32x4_t d_result = vmlaq_f32(d1,d2,d3);
    printf("d_result: [%f %f %f %f] \n",vgetq_lane_f32(d_result,0),vgetq_lane_f32(d_result,1),vgetq_lane_f32(d_result,2),vgetq_lane_f32(d_result,3));


    // 正常指令 长指令 宽指令 窄指令 饱和指令
    int8x8_t  f1 = {1,2,3,4,5,6,7,8};
    int8x8_t  f2 = {3,4,5,6,7,8,9,10};
    int16x8_t f3 = {234,21,34,56,6,7,8,9};
    int16x8_t f4 = {30,3,6,23,7,8,90,21};


    // 长指令 两个窄->长
    int16x8_t f_result1 = vaddl_s8(f1,f2);
    printf("长指令 f_result1: [%d %d %d %d %d %d %d %d] \n",vgetq_lane_s16(f_result1,0),vgetq_lane_s16(f_result1,1),vgetq_lane_s16(f_result1,2),vgetq_lane_s16(f_result1,3),
                                                    vgetq_lane_s16(f_result1,4),vgetq_lane_s16(f_result1,5),vgetq_lane_s16(f_result1,6),vgetq_lane_s16(f_result1,7));
    
    // 正常指令 
    int8x8_t f_result2 = vadd_s8(f1,f2);
    printf("正常指令 f_result2: [%d %d %d %d %d %d %d %d] \n",vget_lane_s8(f_result2,0),vget_lane_s8(f_result2,1),vget_lane_s8(f_result2,2),vget_lane_s8(f_result2,3),
                                                    vget_lane_s8(f_result2,4),vget_lane_s8(f_result2,5),vget_lane_s8(f_result2,6),vget_lane_s8(f_result2,7));

    
    // 宽指令 宽窄->宽
    int16x8_t f_result3 = vaddw_s8(f4,f1);
    printf("宽指令 f_result3: [%d %d %d %d %d %d %d %d] \n",vgetq_lane_s16(f_result3,0),vgetq_lane_s16(f_result3,1),vgetq_lane_s16(f_result3,2),vgetq_lane_s16(f_result3,3),
                                                    vgetq_lane_s16(f_result3,4),vgetq_lane_s16(f_result3,5),vgetq_lane_s16(f_result3,6),vgetq_lane_s16(f_result3,7));
 
    // 窄指令 宽->窄
    // 高位部分相加 结果舍弃
    int8x8_t f_result4 = vaddhn_s16(f3,f4);
    printf("窄指令,结果舍弃 f_result4: [%d %d %d %d %d %d %d %d] \n",vget_lane_s8(f_result4,0),vget_lane_s8(f_result4,1),vget_lane_s8(f_result4,2),vget_lane_s8(f_result4,3),
                                                    vget_lane_s8(f_result4,4),vget_lane_s8(f_result4,5),vget_lane_s8(f_result4,6),vget_lane_s8(f_result4,7));

    // 高位部分相加 结果四舍五入
    int8x8_t f_result5 = vraddhn_s16(f3,f4);
    printf("窄指令,四舍五入 f_result5: [%d %d %d %d %d %d %d %d] \n",vget_lane_s8(f_result5,0),vget_lane_s8(f_result5,1),vget_lane_s8(f_result5,2),vget_lane_s8(f_result5,3),
                                                    vget_lane_s8(f_result5,4),vget_lane_s8(f_result5,5),vget_lane_s8(f_result5,6),vget_lane_s8(f_result5,7));

    uint8x8_t  ff1 = {1,2,3,4,5,6,7,8};
    uint8x8_t  ff2 = {3,4,5,6,7,8,9,254};
    // 饱和指令
    uint8x8_t f_result6 = vqadd_u8(ff1,ff2);
    printf("饱和指令 f_result6: [%d %d %d %d %d %d %d %d] \n",vget_lane_u8(f_result6,0),vget_lane_u8(f_result6,1),vget_lane_u8(f_result6,2),vget_lane_u8(f_result6,3),
                                                    vget_lane_u8(f_result6,4),vget_lane_u8(f_result6,5),vget_lane_u8(f_result6,6),vget_lane_u8(f_result6,7));



    float c[size];

    double latency_ms = 0.0;
    struct timeval start,end1,end2,end3,end4;
    gettimeofday(&start,NULL);

    neon_add(a, b, c, size);
    gettimeofday(&end1,NULL);
    latency_ms = (end1.tv_sec - start.tv_sec) * 1000 + (double)(end1.tv_usec - start.tv_usec)/ 1000.0;
    printf("Latency Neon add is %lf ms\n",latency_ms);
    

    add(a, b, c, size);
    gettimeofday(&end2,NULL);
    latency_ms = (end2.tv_sec - end1.tv_sec) * 1000 + (double)(end2.tv_usec - end1.tv_usec)/ 1000.0;
    printf("Latency add is %lf ms\n",latency_ms);

    neon_multiply(a, b, c, size);
    gettimeofday(&end3,NULL);
    latency_ms = (end3.tv_sec - end2.tv_sec) * 1000 + (double)(end3.tv_usec - end2.tv_usec)/ 1000.0;
    printf("Latency Neon multiply is %lf ms\n",latency_ms);

    multiply(a, b, c, size);
    gettimeofday(&end4,NULL);
    latency_ms = (end4.tv_sec - end3.tv_sec) * 1000 + (double)(end4.tv_usec - end3.tv_usec)/ 1000.0;
    printf("Latency multiply is %lf ms\n",latency_ms);


#if 1
    for (int i = 0; i < size; ++i) {
        printf("%f ", c[i]);
    }
    printf(" \n");
#endif
    return 0;
}
